  precision mediump float;
  precision mediump int;
  uniform mat4 uMVMatrix; // model-view matrix (model -> view space)
  uniform mat4 uPMatrix; // projection matrix (view -> projection space)
  uniform mat4 uNMatrix; // normal matrix (inverse of transpose of model-view matrix)

  uniform mat4 uLightMatrix; // mvpdMatrix of light space (model -> display space)

  attribute vec3 aVertexNormal;
  attribute vec2 aTextureCoord;
  attribute float aVertexEdge; // 0 or 1. 1 if the vertex has an edge. (becuase we can't pass bool to attributes)
  attribute vec3 aPosition;


  attribute vec3 aMultiPurposeVector;

  varying vec3 vPosition;
  varying vec3 vNormal;
  varying vec2 vTextureCoord;
  varying vec4 vLightCoord; // coordinate in light space; to be mapped onto shadow map

  uniform float uEdgeThickness;
  uniform int uEdge;

  uniform int uGenerateShadowMap;

  uniform int uSelfShadow;

  uniform int uAxis;
  uniform int uCenterPoint;

  vec3 qtransform(vec4 q, vec3 v) {
    return v + 2.0 * cross(cross(v, q.xyz) - q.w*v, q.xyz);
  }

  void main() {
    vec3 position;
    vec3 normal;

    if (uAxis==1 || uCenterPoint==1) {

      position = aMultiPurposeVector;

    } else {

      position = aPosition;
      normal = aVertexNormal;
    }

    // return vertex point in projection space
    gl_Position = uPMatrix * uMVMatrix * vec4(position, 1.0);

    if (uCenterPoint==1) {
      gl_Position.z = 0.0; // always on top
      gl_PointSize = 16.0;
    }

    if (uGenerateShadowMap==1 || uAxis==1 || uCenterPoint==1) return;

    // for fragment shader
    vTextureCoord = aTextureCoord;
    vPosition = (uMVMatrix * vec4(position, 1.0)).xyz;
    vNormal = (uNMatrix * vec4(normal, 1.0)).xyz;

    if (uSelfShadow==1) {
      vLightCoord = uLightMatrix * vec4(position, 1.0);
    }

    if (uEdge==1) {
      vec4 pos = gl_Position;
      vec4 pos2 = uPMatrix * uMVMatrix * vec4(position + normal, 1.0);
      vec4 norm = normalize(pos2 - pos);
      gl_Position = pos + norm * uEdgeThickness * aVertexEdge * pos.w; // scale by pos.w to prevent becoming thicker when zoomed
      return;
    }
  }

